Imports System
Imports System.Text
Imports System.Threading
Imports System.Net
Imports System.Net.Sockets
Imports ccapps.persistent.Telnet.Scripting

Namespace persistent.Telnet
    Public MustInherit Class TelnetProtocolHandler
        ' Methods
        Public Sub New()
            Me.Reset()
            Me._crlf(0) = 13
            Me._crlf(1) = 10
            Me._cr(0) = 13
            Me._cr(1) = 0
        End Sub

        Private Sub HandleSB(ByVal type As Byte, ByVal sbdata As Byte(), ByVal sbcount As Integer)
            Dim num As Byte = type
            If ((num = &H18) AndAlso ((sbcount > 0) AndAlso (sbdata(0) = TelnetProtocolHandler.TELQUAL_SEND))) Then
                Me.Write(TelnetProtocolHandler.IACSB)
                Me.Write(CByte(&H18))
                Me.Write(TelnetProtocolHandler.TELQUAL_IS)
                Me.Write(Encoding.ASCII.GetBytes(Me.terminalType))
                Me.Write(TelnetProtocolHandler.IACSE)
            End If
        End Sub

        Protected Sub InputFeed(ByVal b As Byte(), ByVal len As Integer)
            Dim destinationArray As Byte() = New Byte((Me.tempbuf.Length + len) - 1) {}
            Array.Copy(Me.tempbuf, 0, destinationArray, 0, Me.tempbuf.Length)
            Array.Copy(b, 0, destinationArray, Me.tempbuf.Length, len)
            Me.tempbuf = destinationArray
        End Sub

        Protected Function Negotiate(ByVal nbuf As Byte()) As Integer
            Dim length As Integer = Me.tempbuf.Length
            If (length = 0) Then
                Return -1
            End If
            Dim b As Byte() = New Byte(3 - 1) {}
            Dim sbdata As Byte() = New Byte(Me.tempbuf.Length - 1) {}
            Dim tempbuf As Byte() = Me.tempbuf
            Dim sbcount As Integer = 0
            Dim sourceIndex As Integer = 0
            Dim num6 As Integer = 0
            Dim flag As Boolean = False
            Dim flag2 As Boolean = False
            Do While ((Not flag AndAlso (sourceIndex < length)) AndAlso (num6 < nbuf.Length))
                Dim num3 As Byte
                Dim size As Size
                Dim num7 As Integer
                Dim num8 As Integer
                Dim num9 As Integer
                Dim num10 As Byte
                Dim index As Byte = tempbuf(sourceIndex)
                sourceIndex += 1
                If (index >= &H100) Then
                    'index = CByte(Math.Abs((index - &H100)))
                    index = CByte((index - &H100))
                End If
                Select Case Me.neg_state
                    Case 0
                        If (index <> &HFF) Then
                            Exit Select
                        End If
                        Me.neg_state = 1
                        GoTo Continue
                    Case 1
                        num10 = index
                        Select Case num10
                            Case 250
                                GoTo Label_014F
                            Case &HFB
                                GoTo Label_010D
                            Case &HFC
                                GoTo Label_0119
                            Case &HFD
                                GoTo Label_0131
                            Case &HFE
                                GoTo Label_0125
                            Case &HFF
                                GoTo Label_00F3
                            Case &HEF
                                GoTo Label_013D
                        End Select
                        GoTo Label_015E
                    Case 2
                        num8 = sourceIndex
                        GoTo Label_063F
                    Case 3
                        num10 = index
                        Select Case num10
                            Case 0
                                GoTo Label_01AF
                            Case 1
                                GoTo Label_018D
                            Case 2
                                GoTo Label_01B8
                            Case 3
                                GoTo Label_019D
                            Case &H19
                                GoTo Label_01A6
                        End Select
                        GoTo Label_01B8
                    Case 4
                        num10 = index
                        Select Case num10
                            Case 0
                                GoTo Label_0351
                            Case 1
                                GoTo Label_0326
                            Case 2
                                GoTo Label_0472
                            Case 3
                                GoTo Label_0339
                            Case &H18
                                GoTo Label_0345
                            Case &H1F
                                GoTo Label_035D
                        End Select
                        GoTo Label_0472
                    Case 5
                        num10 = index
                        Select Case num10
                            Case 0
                                GoTo Label_0277
                            Case 1
                                GoTo Label_0255
                            Case 2
                                GoTo Label_0280
                            Case 3
                                GoTo Label_0265
                            Case &H19
                                GoTo Label_026E
                        End Select
                        GoTo Label_0280
                    Case 6
                        num10 = index
                        Select Case num10
                            Case 0
                                GoTo Label_0531
                            Case 1
                                GoTo Label_050F
                            Case 2
                                GoTo Label_053A
                            Case 3
                                GoTo Label_051F
                            Case &H1F
                                GoTo Label_0528
                        End Select
                        GoTo Label_053A
                    Case 7
                        num7 = sourceIndex
                        GoTo Label_05D3
                    Case 8
                        num9 = sourceIndex
                        GoTo Label_06AF
                    Case 9
                        num10 = index
                        Select Case num10
                            Case 240
                                GoTo Label_072D
                            Case 250
                                GoTo Label_074C
                        End Select
                        If (num10 <> &HFF) Then
                            GoTo Label_0764
                        End If
                        Me.neg_state = 8
                        sbdata(useAndIncrease(sbcount)) = &HFF
                        GoTo Continue
                    Case Else
                        GoTo Label_076D
                End Select
                nbuf(useAndIncrease(num6)) = index
                GoTo Continue
Label_00F3:
                Me.neg_state = 0
                nbuf(useAndIncrease(num6)) = &HFF
                GoTo Continue
Label_010D:
                Me.neg_state = 3
                GoTo Continue
Label_0119:
                Me.neg_state = 5
                GoTo Continue
Label_0125:
                Me.neg_state = 6
                GoTo Continue
Label_0131:
                Me.neg_state = 4
                GoTo Continue
Label_013D:
                Me.NotifyEndOfRecord()
                Me.neg_state = 0
                GoTo Continue
Label_014F:
                Me.neg_state = 2
                sbcount = 0
                GoTo Continue
Label_015E:
                Me.neg_state = 0
                GoTo Continue
Label_018D:
                num3 = &HFD
                Me.SetLocalEcho(False)
                GoTo Label_01BF
Label_019D:
                num3 = &HFD
                GoTo Label_01BF
Label_01A6:
                num3 = &HFD
                GoTo Label_01BF
Label_01AF:
                num3 = &HFD
                GoTo Label_01BF
Label_01B8:
                num3 = &HFE
Label_01BF:
                If ((num3 <> Me.sentDX((index + &H80))) OrElse (&HFB <> Me.receivedWX((index + &H80)))) Then
                    b(0) = &HFF
                    b(1) = num3
                    b(2) = index
                    Me.Write(b)
                    Me.sentDX((index + &H80)) = num3
                    Me.receivedWX((index + &H80)) = &HFB
                End If
                Me.neg_state = 0
                GoTo Continue
Label_0255:
                Me.SetLocalEcho(True)
                num3 = &HFE
                GoTo Label_0287
Label_0265:
                num3 = &HFE
                GoTo Label_0287
Label_026E:
                num3 = &HFE
                GoTo Label_0287
Label_0277:
                num3 = &HFE
                GoTo Label_0287
Label_0280:
                num3 = &HFE
Label_0287:
                If ((num3 <> Me.sentDX((index + &H80))) OrElse (&HFC <> Me.receivedWX((index + &H80)))) Then
                    b(0) = &HFF
                    b(1) = num3
                    b(2) = index
                    Me.Write(b)
                    Me.sentDX((index + &H80)) = num3
                    Me.receivedWX((index + &H80)) = &HFB
                End If
                Me.neg_state = 0
                GoTo Continue
Label_0326:
                num3 = &HFB
                Me.SetLocalEcho(True)
                GoTo Label_0479
Label_0339:
                num3 = &HFB
                GoTo Label_0479
Label_0345:
                num3 = &HFB
                GoTo Label_0479
Label_0351:
                num3 = &HFB
                GoTo Label_0479
Label_035D:
                size = Me.windowSize
                Me.receivedDX(index) = &HFD
                If (Not size.GetType Is GetType(Size)) Then
                    Me.Write(CByte(&HFF))
                    Me.Write(CByte(&HFC))
                    Me.Write(CByte(&H1F))
                    num3 = &HFC
                    Me.sentWX(index) = &HFC
                Else
                    num3 = &HFB
                    Me.sentWX(index) = &HFB
                    b(0) = &HFF
                    b(1) = &HFB
                    b(2) = &H1F
                    Me.Write(b)
                    Me.Write(CByte(&HFF))
                    Me.Write(CByte(250))
                    Me.Write(CByte(&H1F))
                    Me.Write(CByte((size.Width >> 8)))
                    Me.Write(CByte((size.Width And &HFF)))
                    Me.Write(CByte((size.Height >> 8)))
                    Me.Write(CByte((size.Height And &HFF)))
                    Me.Write(CByte(&HFF))
                    Me.Write(CByte(240))
                End If
                GoTo Label_0479
Label_0472:
                num3 = &HFC
Label_0479:
                If ((num3 <> Me.sentWX((&H80 + index))) OrElse (&HFD <> Me.receivedDX((&H80 + index)))) Then
                    b(0) = &HFF
                    b(1) = num3
                    b(2) = index
                    Me.Write(b)
                    Me.sentWX((index + &H80)) = num3
                    Me.receivedDX((index + &H80)) = &HFD
                End If
                Me.neg_state = 0
                GoTo Continue
Label_050F:
                num3 = &HFC
                Me.SetLocalEcho(False)
                GoTo Label_0541
Label_051F:
                num3 = &HFC
                GoTo Label_0541
Label_0528:
                num3 = &HFC
                GoTo Label_0541
Label_0531:
                num3 = &HFC
                GoTo Label_0541
Label_053A:
                num3 = &HFC
Label_0541:
                If ((num3 <> Me.sentWX((index + &H80))) OrElse (&HFE <> Me.receivedDX((index + &H80)))) Then
                    b(0) = &HFF
                    b(1) = num3
                    b(2) = index
                    Me.Write(b)
                    Me.sentWX((index + &H80)) = num3
                    Me.receivedDX((index + &H80)) = &HFE
                End If
                Me.neg_state = 0
                GoTo Continue
Label_05BA:
                If (Me.tempbuf(num7) = 240) Then
                    flag2 = True
                End If
                num7 += 1
Label_05D3:
                If (num7 < Me.tempbuf.Length) Then
                    GoTo Label_05BA
                End If
                If Not flag2 Then
                    sourceIndex -= 1
                    flag = True
                Else
                    flag2 = False
                    If (index = &HFF) Then
                        sbcount = 0
                        Me.current_sb = index
                        Me.neg_state = 8
                    Else
                        Me.neg_state = 0
                    End If
                End If
                GoTo Continue
Label_0626:
                If (Me.tempbuf(num8) = 240) Then
                    flag2 = True
                End If
                num8 += 1
Label_063F:
                If (num8 < Me.tempbuf.Length) Then
                    GoTo Label_0626
                End If
                If Not flag2 Then
                    sourceIndex -= 1
                    flag = True
                Else
                    flag2 = False
                    num10 = index
                    If (num10 = &HFF) Then
                        Me.neg_state = 7
                    Else
                        Me.current_sb = index
                        sbcount = 0
                        Me.neg_state = 8
                    End If
                End If
                GoTo Continue
Label_0696:
                If (Me.tempbuf(num9) = 240) Then
                    flag2 = True
                End If
                num9 += 1
Label_06AF:
                If (num9 < Me.tempbuf.Length) Then
                    GoTo Label_0696
                End If
                If Not flag2 Then
                    sourceIndex -= 1
                    flag = True
                Else
                    flag2 = False
                    num10 = index
                    If (num10 = &HFF) Then
                        Me.neg_state = 9
                    Else
                        sbdata(useAndIncrease(sbcount)) = index
                    End If
                End If
                GoTo Continue
Label_072D:
                Me.HandleSB(Me.current_sb, sbdata, sbcount)
                Me.current_sb = 0
                Me.neg_state = 0
                GoTo Continue
Label_074C:
                Me.HandleSB(Me.current_sb, sbdata, sbcount)
                Me.neg_state = 2
                GoTo Continue
Label_0764:
                Me.neg_state = 0
                GoTo Continue
Label_076D:
                Me.neg_state = 0
Continue:
            Loop
            Dim destinationArray As Byte() = New Byte((length - sourceIndex) - 1) {}
            Array.Copy(Me.tempbuf, sourceIndex, destinationArray, 0, (length - sourceIndex))
            Me.tempbuf = destinationArray
            Return num6
        End Function

        Protected MustOverride Sub NotifyEndOfRecord()

        Protected Sub Reset()
            Me.neg_state = 0
            Me.receivedDX = New Byte(&H100 - 1) {}
            Me.sentDX = New Byte(&H100 - 1) {}
            Me.receivedWX = New Byte(&H100 - 1) {}
            Me.sentWX = New Byte(&H100 - 1) {}
        End Sub

        Protected Sub SendTelnetControl(ByVal code As Byte)
            Dim b As Byte() = New Byte() {&HFF, code}
            Me.Write(b)
        End Sub

        Protected MustOverride Sub SetLocalEcho(ByVal echo As Boolean)

        Protected Sub Transpose(ByVal buf As Byte())
            Dim buffer2 As Byte()
            Dim length As Integer = 0
            Dim sourceArray As Byte() = New Byte((buf.Length * 2) - 1) {}
            Dim i As Integer
            For i = 0 To buf.Length - 1
                Dim num5 As Byte = buf(i)
                If (num5 <> 10) Then
                    If (num5 = 13) Then
                        GoTo Label_00C6
                    End If
                    If (num5 <> &HFF) Then
                        GoTo Label_0137
                    End If
                    sourceArray(useAndIncrease(length)) = &HFF
                    sourceArray(useAndIncrease(length)) = &HFF
                ElseIf (Me.receivedDX(&H80) <> &HFD) Then
                    Do While ((sourceArray.Length - length) < Me.CRLF.Length)
                        buffer2 = New Byte((sourceArray.Length * 2) - 1) {}
                        Array.Copy(sourceArray, 0, buffer2, 0, length)
                        sourceArray = buffer2
                    Loop
                    Dim j As Integer
                    For j = 0 To Me.CRLF.Length - 1
                        sourceArray(useAndIncrease(length)) = Me._crlf(j)
                    Next j
                Else
                    sourceArray(useAndIncrease(length)) = buf(i)
                End If
                GoTo Continue
Label_00C6:
                If (Me.receivedDX(&H80) <> &HFD) Then
                    Do While ((sourceArray.Length - length) < Me.CR.Length)
                        buffer2 = New Byte((sourceArray.Length * 2) - 1) {}
                        Array.Copy(sourceArray, 0, buffer2, 0, length)
                        sourceArray = buffer2
                    Loop
                    Dim k As Integer
                    For k = 0 To Me.CR.Length - 1
                        sourceArray(useAndIncrease(length)) = Me._cr(k)
                    Next k
                Else
                    sourceArray(useAndIncrease(length)) = buf(i)
                End If
                GoTo Continue
Label_0137:
                sourceArray(useAndIncrease(length)) = buf(i)
Continue:
            Next i
            buffer2 = New Byte(length - 1) {}
            Array.Copy(sourceArray, 0, buffer2, 0, length)
            Me.Write(buffer2)
        End Sub

        Private Function useAndIncrease(ByRef number As Integer) As Integer
            Dim retVal As Integer = Integer.Parse(number.ToString())
            number += 1
            Return retVal
        End Function

        Protected MustOverride Sub Write(ByVal b As Byte())

        Private Sub Write(ByVal b As Byte)
            Me.Write(New Byte() {b})
        End Sub


        ' Properties
        Public Property CR() As String
            Get
                Return Encoding.ASCII.GetString(Me._cr)
            End Get
            Set(ByVal value As String)
                Me._cr = Encoding.ASCII.GetBytes(value)
            End Set
        End Property

        Public Property CRLF() As String
            Get
                Return Encoding.ASCII.GetString(Me._crlf)
            End Get
            Set(ByVal value As String)
                Me._crlf = Encoding.ASCII.GetBytes(value)
            End Set
        End Property


        ' Fields
        Private _cr As Byte() = New Byte(2 - 1) {}
        Private _crlf As Byte() = New Byte(2 - 1) {}
        Private current_sb As Byte
        Private Const [DO] As Byte = &HFD
        Private Const DONT As Byte = &HFE
        Private Const EOR As Byte = &HEF
        Private Const IAC As Byte = &HFF
        Private Shared IACDO As Byte() = New Byte() {&HFF, &HFD}
        Private Shared IACDONT As Byte() = New Byte() {&HFF, &HFE}
        Private Shared IACSB As Byte() = New Byte() {&HFF, 250}
        Private Shared IACSE As Byte() = New Byte() {&HFF, 240}
        Private Shared IACWILL As Byte() = New Byte() {&HFF, &HFB}
        Private Shared IACWONT As Byte() = New Byte() {&HFF, &HFC}
        Private neg_state As Byte = 0
        Private receivedDX As Byte()
        Private receivedWX As Byte()
        Private Const SB As Byte = 250
        Private Const SE As Byte = 240
        Private sentDX As Byte()
        Private sentWX As Byte()
        Private Const STATE_DATA As Byte = 0
        Private Const STATE_IAC As Byte = 1
        Private Const STATE_IACDO As Byte = 4
        Private Const STATE_IACDONT As Byte = 6
        Private Const STATE_IACSB As Byte = 2
        Private Const STATE_IACSBDATA As Byte = 8
        Private Const STATE_IACSBDATAIAC As Byte = 9
        Private Const STATE_IACSBIAC As Byte = 7
        Private Const STATE_IACWILL As Byte = 3
        Private Const STATE_IACWONT As Byte = 5
        Private Const TELOPT_BINARY As Byte = 0
        Private Const TELOPT_ECHO As Byte = 1
        Private Const TELOPT_EOR As Byte = &H19
        Private Const TELOPT_NAWS As Byte = &H1F
        Private Const TELOPT_SGA As Byte = 3
        Private Const TELOPT_TTYPE As Byte = &H18
        Private Shared TELQUAL_IS As Byte = 0
        Private Shared TELQUAL_SEND As Byte = 1
        Private tempbuf As Byte() = New Byte(0 - 1) {}
        Protected terminalType As String = "dumb"
        Private Const WILL As Byte = &HFB
        Protected windowSize As Size = Size.Empty
        Private Const WONT As Byte = &HFC
    End Class

    Public Class TelnetWrapper
        Inherits TelnetProtocolHandler
        Private _RemoteEP As IPEndPoint
        ' Events
        Public Event DataAvailable As DataAvailableEventHandler
        Public Event ServerRespond As ServerRespondEventHandler
        Public Event Disconnected As DisconnectedEventHandler

        Public Sub New(ByVal host As String, ByVal port As Integer)
            Me._hostname = host
            Me._port = port
            Dim address As IPAddress = Dns.Resolve(host).AddressList(0)
            Me._RemoteEP = New IPEndPoint(address, port)
            Me.socket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
        End Sub

        Public Sub Close()
            Me.Dispose()
        End Sub

        Public Sub Connect()
            Try
                Me.socket.BeginConnect(Me._RemoteEP, New AsyncCallback(AddressOf Me.ConnectCallback), Me.socket)
                Me.connectDone.WaitOne()
                MyBase.Reset()
            Catch obj1 As Exception
                Me.Disconnect()
                Throw
            End Try
        End Sub

        Private Sub ConnectCallback(ByVal ar As IAsyncResult)
            Dim socket As Socket = Nothing
            Try
                DirectCast(ar.AsyncState, Socket).EndConnect(ar)
                Me.connectDone.Set()
            Catch exception As Exception
                Me.Disconnect()
                Me.connectDone.Set()
                Throw New ApplicationException(("Unable to connect to " & socket.RemoteEndPoint.ToString), exception)
            End Try
        End Sub

        Public Sub Disconnect()
            If ((Not Me.socket Is Nothing) AndAlso Me.socket.Connected) Then
                Me.socket.Shutdown(SocketShutdown.Both)
                Me.socket.Close()
                RaiseEvent Disconnected(Me, New EventArgs)
            End If
        End Sub

        Public Sub Dispose()
            GC.SuppressFinalize(Me)
            Me.Dispose(True)
        End Sub

        Protected Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
                Me.Disconnect()
            End If
        End Sub

        Protected Overrides Sub Finalize()
            Try
                Me.Dispose(False)
            Finally
                MyBase.Finalize()
            End Try
        End Sub

        Protected Overrides Sub NotifyEndOfRecord()
        End Sub

        Public Sub Receive(ByVal waitNext As Boolean)
            Me.Receive(Me.socket, waitNext)
        End Sub

        Private Sub Receive(ByVal client As socket, ByVal waitNext As Boolean)
            Try
                Dim state As New State
                state.WorkSocket = client
                state.WaitNext = waitNext
                client.BeginReceive(state.Buffer, 0, &H100, SocketFlags.None, New AsyncCallback(AddressOf Me.ReceiveCallback), state)
            Catch exception As Exception
                Me.Disconnect()
                Throw New ApplicationException("Error on read from socket.", exception)
            End Try
        End Sub

        Private Sub ReceiveCallback(ByVal ar As IAsyncResult)
            Try
                Dim asyncState As State = DirectCast(ar.AsyncState, State)
                Dim workSocket As Socket = asyncState.WorkSocket
                If workSocket Is Nothing OrElse Not workSocket.Connected Then Return
                Dim len As Integer = workSocket.EndReceive(ar)
                If (len > 0) Then
                    MyBase.InputFeed(asyncState.Buffer, len)
                    MyBase.Negotiate(asyncState.Buffer)
                    RaiseEvent DataAvailable(Me, New DataAvailableEventArgs(Encoding.ASCII.GetString(asyncState.Buffer, 0, len)))
                    If asyncState.WaitNext Then workSocket.BeginReceive(asyncState.Buffer, 0, &H100, SocketFlags.None, New AsyncCallback(AddressOf Me.ReceiveCallback), asyncState)
                Else
                    Me.Disconnect()
                End If
            Catch exception As Exception
                Me.Disconnect()
                Throw New ApplicationException("Error reading from socket.", exception)
            End Try
        End Sub






        Private Sub ReceiveExpected(ByVal state As State)
            Try
                state.WorkSocket.BeginReceive(state.Buffer, 0, state.BufferSize, SocketFlags.None, New AsyncCallback(AddressOf Me.ReceiveExpectedCallback), state)
            Catch exception As Exception
                Me.Disconnect()
                Throw New ApplicationException("Error on read from socket.", exception)
            End Try
        End Sub

        Private Sub ReceiveExpectedCallback(ByVal ar As IAsyncResult)
            Dim asyncState As State = DirectCast(ar.AsyncState, State)
            Dim workSocket As Socket = asyncState.WorkSocket

            If workSocket Is Nothing OrElse Not workSocket.Connected Then Return
            Try

                Dim len As Integer = workSocket.EndReceive(ar)
                If (len > 0) Then
                    MyBase.InputFeed(asyncState.Buffer, len)
                    MyBase.Negotiate(asyncState.Buffer)
                    Dim response As String = Encoding.ASCII.GetString(asyncState.Buffer, 0, len)

                    Dim args As New ServerResponseEventArgs(asyncState.Line, Encoding.ASCII.GetString(asyncState.Buffer, 0, len))
                    RaiseEvent ServerRespond(Me, args)

                    If Not args.IsExpectedResponse Then
                        workSocket.BeginReceive(asyncState.Buffer, 0, State.BufferSize, SocketFlags.None, New AsyncCallback(AddressOf Me.ReceiveExpectedCallback), asyncState)
                    End If

                Else
                    Me.Disconnect()
                End If
            Catch exception As Exception
                Me.Disconnect()
                Throw New ApplicationException("Error reading from socket.", exception)
            End Try
        End Sub

        Public Sub ExecuteExpected(ByVal line As ScriptLine)
            Try
                Dim bytes As Byte() = Encoding.ASCII.GetBytes(line._Command)
                Dim state As New State
                state.WorkSocket = Me.socket
                state.Line = line
                MyBase.Transpose(bytes)
                If line._Command <> "exit" Then
                    ReceiveExpected(state)
                Else
                    Receive(False)
                End If
            Catch exception As Exception
                Me.Disconnect()
                Throw New ApplicationException("Error writing to socket.", exception)
            End Try
        End Sub




        Public Function Send(ByVal cmd As String) As String
            Try
                Dim bytes As Byte() = Encoding.ASCII.GetBytes(cmd)
                MyBase.Transpose(bytes)
            Catch exception As Exception
                Me.Disconnect()
                Throw New ApplicationException("Error writing to socket.", exception)
            End Try
            Return Nothing
        End Function

        Private Sub Send(ByVal client As socket, ByVal byteData As Byte())
            client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, New AsyncCallback(AddressOf Me.SendCallback), client)
        End Sub

        Private Sub SendCallback(ByVal ar As IAsyncResult)
            DirectCast(ar.AsyncState, Socket).EndSend(ar)
            Me.sendDone.Set()
        End Sub

        Protected Overrides Sub SetLocalEcho(ByVal echo As Boolean)
        End Sub

        Protected Overrides Sub Write(ByVal b As Byte())
            If Me.socket.Connected Then
                Me.Send(Me.socket, b)
            End If
            Me.sendDone.WaitOne()
        End Sub


        ' Properties
        Public ReadOnly Property Connected() As Boolean
            Get
                Return Me.socket.Connected
            End Get
        End Property

        Public Property Hostname() As String
            Get
                Return Me._hostname
            End Get
            Set(ByVal value As String)
                Me._hostname = value
            End Set
        End Property

        Public Property Port() As Integer
            Get
                Return Me._port
            End Get
            Set(ByVal value As Integer)
                If (value <= 0) Then
                    Throw New ArgumentException("Port number must be greater than 0.", "Port")
                End If
                Me._port = value
            End Set
        End Property

        Public WriteOnly Property TerminalHeight() As Integer
            Set(ByVal value As Integer)
                Me.windowSize.Height = value
            End Set
        End Property

        Public Shadows WriteOnly Property TerminalType() As String
            Set(ByVal value As String)
                MyBase.terminalType = value
            End Set
        End Property

        Public WriteOnly Property TerminalWidth() As Integer
            Set(ByVal value As Integer)
                Me.windowSize.Width = value
            End Set
        End Property


        ' Fields
        Private connectDone As ManualResetEvent = New ManualResetEvent(False)
        Protected _hostname As String
        Protected _port As Integer
        Private sendDone As ManualResetEvent = New ManualResetEvent(False)
        Private socket As socket
    End Class

    Public Class DataAvailableEventArgs
        Inherits EventArgs
        Private _data As String

        Public Sub New(ByVal output As String)
            Me._data = output
        End Sub

        Public ReadOnly Property Data() As String
            Get
                Return Me._data
            End Get
        End Property
    End Class

    Public Class ServerResponseEventArgs
        Inherits EventArgs

        Private _response As String
        Private _command As ScriptLine
        Private _isExpectedResponse As Boolean = False

        Public Sub New(ByVal command As ScriptLine, ByVal response As String)
            Me._response = response
            Me._command = command
        End Sub

        Public ReadOnly Property Response() As String
            Get
                Return Me._response
            End Get
        End Property

        Public ReadOnly Property Command() As ScriptLine
            Get
                Return Me._command
            End Get
        End Property

        Public Property IsExpectedResponse() As Boolean
            Get
                Return Me._isExpectedResponse
            End Get
            Set(ByVal Value As Boolean)
                Me._isExpectedResponse = Value
            End Set
        End Property
    End Class

    Public Class State
        ' Fields
        Public Buffer As Byte() = New Byte(&H100 - 1) {}
        Public Const BufferSize As Integer = &H100
        Public WorkSocket As Socket = Nothing
        Public WaitNext As Boolean = True
        Public Line As ScriptLine
    End Class

    Public Class TelnetConnectionException
        Inherits Exception

        Public Sub New(ByVal message As String, ByVal innerException As Exception)
            MyBase.New(message, innerException)
        End Sub
    End Class

    Public Delegate Sub DataAvailableEventHandler(ByVal sender As Object, ByVal e As DataAvailableEventArgs)
    Public Delegate Sub ServerRespondEventHandler(ByVal sender As Object, ByVal e As ServerResponseEventArgs)

    Public Delegate Sub DisconnectedEventHandler(ByVal sender As Object, ByVal e As EventArgs)
End Namespace

